import jade.core.Actor;
import jade.fov.FoV;
import jade.fov.FoV.FoVFactory;
import jade.util.Coord;
import jade.util.Direction;

import java.awt.Color;
import java.util.Collection;
import java.util.logging.Logger;

abstract class Creature extends Actor {
	private transient PrisonEscape parent;
	private Logger logger = Logger.getLogger(PrisonEscape.class.getName());
	
	class Stat {
		private int value;
		private int base;

		Stat(int base) {
			this(base, base);
		}

		Stat(int base, int value) {
			this.base = base;
			this.value = value;
		}

		void buff(int buff) {
			value += buff;
		}

		void buffBase(int buff) {
			base += buff;
		}

		void cappedBuff(int buff) {
			value = Math.min(value + buff, base);
		}

		@Override
		public String toString() {
			return Integer.valueOf(value).toString();
		}

		int value() {
			return value;
		}
	}

	private String name;
	private int maxHp;
	private Stat hp;
	private Stat atk;
	private Stat def;
	private Stat dmg;
	Dungeon dungeon;
	boolean move;
	Collection<Coord> fov;
	int visionRange;

	Creature(final PrisonEscape parent, char face, String name, Dungeon dungeon, Color color, int hp, int atk,
			int def, int dmg) {
		super(face, color);
		this.parent = parent;
		this.name = name;
		this.dungeon = dungeon;
		this.maxHp = hp;
		this.hp = new Stat(hp) {
			@Override
			public void buff(int buff) {
				super.buff(buff);
				if (value() < 1)
					expire();
			}
		};
		this.atk = new Stat(atk);
		this.def = new Stat(def);
		this.dmg = new Stat(dmg);
		move = false;
	}

	private void attack(Creature bump) {
		logger.info("attack");
		int hp = bump.getHp().value();
		if (dungeon.random.nextFloat() < (float) atk.value
				/ (atk.value + bump.def.value))
			bump.getHp().buff(-dmg.value());
		
		if (hp == bump.getHp().value()){
			parent.playSounds(Sound.MISS);
			appendMessage(this + (this instanceof Player?" miss ":" misses ") + bump);
		}
		else{
			appendMessage(this + (bump.isExpired() ? (this instanceof Player?" kill ":" kills ") : (this instanceof Player?" hit ":" hits "))
					+ bump);
			//FIXME: NullPointer Exception?!
			//parent.playSounds(Sound.KILL);
		}
	}

	public void calcFoV() {
		FoV circularShadowFov = FoVFactory.get(FoVFactory.CircularShadow);
		fov = circularShadowFov
				.calcFoV(getWorld(), getX(), getY(), visionRange);
	}

	Stat getHp() {
		return hp;
	}
	
	int getMaxHp() {
		return maxHp;
	}

	boolean moveOrSlide(Direction dir) {
		Direction left = dir.prev();
		Direction right = dir.next();

		boolean moved = tryToMove(dir.dx, dir.dy);
		if (!moved)
			moved = tryToMove(left.dx, left.dy);
		if (!moved)
			moved = tryToMove(right.dx, right.dy);
		
		return moved;
	}

	private boolean tryToMove(int dx, int dy) {
		int x = getX() + dx;
		int y = getY() + dy;
		Creature bumped = getWorld().getActorAt(x, y, Creature.class);
		if (bumped != null && bumped != this
				&& (bumped instanceof Player || this instanceof Player)) {
			attack(bumped);
			return true;
		} else if (getWorld().isPassable(getX() + dx, getY() + dy)) {
			super.move(dx, dy);
			return true;
		} else
			return false;
	}
	
	@Override
	public String toString() {
		return name;
	}
}
